#include "ZWidget.h"

HANDLE hStdout, hStdin; 
DWORD cRead, cWritten, fdwMode, fdwOldMode;

/// class static member / constants /////////////////////////////////////////////
int Display::dummy = init();
Display * Display::display = NULL;


const int Toolkit::FOREGROUND_WINDOW_STATE = 0x70;
const int Toolkit::BACKGROUND_WINDOW_STATE = 0x10;

const int Toolkit::FOREGROUND_TEXT_STATE = 0x12;
const int Toolkit::BACKGROUND_TEXT_STATE = 0x34;

/// class implementation /////////////////////////////////////////////////////////

Display::Display(int w, int h) : Containable(0,0), screenWidth(w), screenHeight(h) {
	dwBufferSize.X = screenWidth;	dwBufferSize.Y = screenHeight;
	dwBufferCoord.X = dwBufferCoord.Y = 0;
	rcRegion.Left = rcRegion.Top = 0;
	rcRegion.Right = screenWidth-1;
	rcRegion.Bottom = screenHeight-1;		

	buffer = new CHAR_INFO[w*h];
	SetConsoleScreenBufferSize( hStdout, dwBufferSize);
	ReadConsoleOutput( hStdout, (CHAR_INFO *)buffer, dwBufferSize,
		dwBufferCoord, &rcRegion );
}

void Display::flush() {
	WriteConsoleOutput( hStdout, (CHAR_INFO *)buffer, dwBufferSize,
		dwBufferCoord, &rcRegion );
}

WORD Display::getVirtualKey(void) {
	INPUT_RECORD event;
	BOOL         done = FALSE;
	unsigned int counter = 0; 

	while(true) {
		if (WaitForSingleObject( hStdin, 0 ) == WAIT_OBJECT_0)  /* if kbhit */ {
			DWORD count;  /* ignored */
			/* Get the input event */
			ReadConsoleInput( hStdin, &event, 1, &count );
			/* Only respond to key release events */
			if ((event.EventType == KEY_EVENT)
				&&  !event.Event.KeyEvent.bKeyDown) {
					return event.Event.KeyEvent.wVirtualKeyCode;
			}
		}
	}
}


int Display::init(void) {
	hStdin = GetStdHandle(STD_INPUT_HANDLE); 
	hStdout = GetStdHandle(STD_OUTPUT_HANDLE); 
	if (hStdin == INVALID_HANDLE_VALUE || 
		hStdout == INVALID_HANDLE_VALUE) 
	{
		MessageBox(NULL, TEXT("GetStdHandle"),
			TEXT("Console Error"), MB_OK);
		return -1;
	}
	return 0;
}
void Display::drawBox(int x, int y, int w, int h, int state) {
	int i,j;
	CHAR_INFO * p;
	
	for(i=max(x,0);i<min(x+w,this->screenWidth);i++) {
		for(j=max(y,0);j<min(y+h,this->screenHeight);j++) {			
			p = this->at(j, i);

			p->Char.AsciiChar = ' ';
			p->Attributes = state;
		}
	}
	return;
}

void Display::drawString(int x, int y, int w, int h, const string & text) {
	int i,j;
	CHAR_INFO *p;
	unsigned int l = text.length();
	for(i=min(max(x,0),this->screenWidth);i<max(min(min(x+w,this->screenWidth),x+l),0);i++) {
		for(j=max(y,0);j<min(y+h,this->screenHeight);j++) {			
			p = this->at(j, i);
			p->Char.AsciiChar = text[(i-x)];
		}
	}
	return;
}

void Display::render() {
	// clear screen
	drawBox(0,0,screenWidth,screenHeight,0x00);

	unsigned int i;
	for(i=0;i<list.size();i++) {
		Drawable * d;
		d = dynamic_cast<Drawable*>(list[i]);
		if (d == focus) {
			continue;
		}
		if (d!=NULL) {
			d->draw(this, false);
		}
	}
	const Drawable * d;
	d = dynamic_cast<const Drawable*>(focus);
	if (d != NULL ){
		d->draw(this, true);
	}
	flush();
}

void Window::draw(const Containable * containable, bool isFocus_) const {
	int C = 0x80; // shade
	int bx, by;
	int state = isFocus_? fstate: bstate;
	bx = containable->getX();
	by = containable->getY();
	Display::getDisplay()->drawBox(x+bx,y+by,w_-2,h_-1, state);
	Display::getDisplay()->drawBox(x+2+bx,y+h_-1+by,w_-2,1, C);
	Display::getDisplay()->drawBox(x+w_-2+bx,y+1+by,2,h_-1, C);

	unsigned int i;
	for(i=0;i<list.size();i++) {
		Drawable * d;
		d = dynamic_cast<Drawable*>(list[i]);
		if (d == focus) {
			continue;
		}
		if (d!=NULL) {
			d->draw(this, false);
		}
	}
	const Drawable * d;
	d = dynamic_cast<const Drawable*>(focus);
	if (d != NULL ){
		d->draw(this, true);
	}
}

void Text::draw(const Containable * containable, bool isFocus_) const {
	int C = 0x80; // shade
	int bx, by;
	int state = isFocus_? fstate: bstate;
	bx = containable->getX();
	by = containable->getY();
	Display::getDisplay()->drawBox(bx+x,by+y,w,h,state);
	Display::getDisplay()->drawString(bx+x,by+y,w,h, text);
	return;
}